package pl.edu.mimuw.irs.rcp.persistence;

import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.FileEditorInput;

import pl.edu.mimuw.irs.core.model.design.net.IRSWorkflow;
import pl.edu.mimuw.irs.core.model.design.resource.Resources;
import pl.edu.mimuw.irs.core.model.design.resource.Roles;
import pl.edu.mimuw.irs.rcp.controller.editor.bucket.Bucket;
import pl.edu.mimuw.irs.rcp.persistence.bucket.BucketPersistence;
import pl.edu.mimuw.irs.rcp.persistence.descriptor.HistoryFilesDescriptor;
import pl.edu.mimuw.irs.rcp.persistence.resource.ResourcesPersistence;
import pl.edu.mimuw.irs.rcp.persistence.workflow.IRSWorkflowPersistence;
import pl.edu.mimuw.irs.rcp.util.Logger;
import pl.edu.mimuw.irs.rcp.util.MessageCode;

public class PersistenceManager {

	// *** Static properties

	public static final QualifiedName SESSION_WORKFLOW = new QualifiedName("pl.edu.mimuw.irs", "Workflow");
	public static final QualifiedName SESSION_RESOURCES = new QualifiedName("pl.edu.mimuw.irs", "Resources");
	public static final QualifiedName SESSION_HISTORY = new QualifiedName("pl.edu.mimuw.irs", "History");
	public static final QualifiedName SESSION_BUCKET = new QualifiedName("pl.edu.mimuw.irs", "Bucket");
	
	// *** Static attributes
	
	private static Map<IProject, PersistenceManager> map;
	public static Map<IProject, PersistenceManager> getMap() {
		if (map == null) map = new LinkedHashMap<IProject, PersistenceManager>();
		return map;
	}
	
	// *** Static methods
	
	public static PersistenceManager getInstance(IProject project, IProgressMonitor progress) throws CoreException {
		if (project == null) return null;
		PersistenceManager manager = getMap().get(project);
		if (manager == null) {
			manager = new PersistenceManager(project, progress);
			getMap().put(project, manager);
		}
		return manager;
	}
	
	public static PersistenceManager getInstance(IProject project) throws CoreException {
		return getInstance(project, null);
	}
	
	public static PersistenceManager getInstance(IEditorInput input, IProgressMonitor progress) throws CoreException {
		if (!(input instanceof FileEditorInput)) return null;
		FileEditorInput fileEditorInput = (FileEditorInput)input;
		IFile file = fileEditorInput.getFile();
		if (file == null) return null;
		IProject project = file.getProject();
		return getInstance(project, progress);		
	}
	
	public static PersistenceManager getInstance(IEditorInput input) throws CoreException {
		return getInstance(input, null);	
	}
	
	// *** Attributes

	private IProject project;
	public IProject getProject() {
		return project;
	}
	
	// *** Constructors

	private PersistenceManager(IProject project, IProgressMonitor progress) throws CoreException {
		this.project = project;
		project.open(progress);
	}
	
	// *** Methods
	
	public void close(IProgressMonitor progress) throws CoreException {
		project.close(progress);
	}
	
	public void close() throws CoreException {
		close(null);
		map.remove(getProject());
	}
	
	public IRSWorkflow getWorkflow() {
		if (getProject() == null) return null;
		/*
		 * We have project, now we have to get workflow from session property
		 * or create it if we are first.
		 */
		IRSWorkflow workflow = null;
		try {
			workflow = (IRSWorkflow) getProject().getSessionProperty(SESSION_WORKFLOW);
		} catch (CoreException e) {
			Logger.handle(MessageCode.WORKFLOW_LOAD_DESERIALIZE_PROBLEM, e, SESSION_WORKFLOW);
			return null;
		}
		if (workflow == null) {
			IFile workflowFile = project.getFile(IRSWorkflowPersistence.FILE);
			if (workflowFile == null) return null;
			try {
				InputStream stream = workflowFile.getContents();
				IPersistence<IRSWorkflow> manager = PersistenceAdapter.getManager(IRSWorkflow.class);
				workflow = manager.loadFromStream(stream);
			} catch (CoreException e) {
				Logger.handle(MessageCode.WORKFLOW_LOAD_DESERIALIZE_PROBLEM, e, workflowFile.getName());
				return null;
			} catch (Exception e) {
				Logger.handle(MessageCode.WORKFLOW_LOAD_DESERIALIZE_PROBLEM, e, workflowFile.getName());
				return null;
			}
			
			try {
				project.setSessionProperty(SESSION_WORKFLOW, workflow);
			} catch (CoreException e) {
				Logger.handle(MessageCode.ROLES_SAVE_SERIALIZE_PROBLEM, e, SESSION_RESOURCES);
				return null;
			}
		}
		
		return workflow;
	}
	
	public void clearWorkflow() {
		if (getProject() == null) return;
		try {
			project.setSessionProperty(SESSION_WORKFLOW, null);
		} catch (CoreException e) {
			Logger.handle(MessageCode.WORKFLOW_CLEAR_PROBLEM, e, SESSION_WORKFLOW);
			return;
		}
	}
	
	public Roles getRoles() {
		if (getProject() == null) return null;
		/*
		 * We have project, now we have to get roles from session property
		 * or create it if we are first.
		 */
		Roles roles = null;
		try {
			IRSWorkflow workflow = (IRSWorkflow) getProject().getSessionProperty(SESSION_WORKFLOW);
			if (workflow != null) roles = workflow.getRoles();
		} catch (CoreException e) {
			Logger.handle(MessageCode.ROLES_LOAD_DESERIALIZE_PROBLEM, e, SESSION_WORKFLOW);
			return null;
		}
//		if (roles == null) {
//			IFile resourcesFile = project.getFile(RolesPersistence.FILE);
//			if (resourcesFile == null) return null;
//			try {
//				InputStream stream = resourcesFile.getContents();
//				IPersistence<Roles> manager = PersistenceAdapter.getManager(Roles.class);
//				roles = manager.loadFromStream(stream);
//			} catch (CoreException e) {
//				Logger.handle(MessageCode.ROLES_LOAD_DESERIALIZE_PROBLEM, e, resourcesFile.getName());
//				return null;
//			} catch (Exception e) {
//				Logger.handle(MessageCode.ROLES_LOAD_DESERIALIZE_PROBLEM, e, resourcesFile.getName());
//				return null;
//			}
//			
//			try {
//				project.setSessionProperty(SESSION_ROLES, roles);
//			} catch (CoreException e) {
//				Logger.handle(MessageCode.ROLES_SAVE_SERIALIZE_PROBLEM, e, SESSION_RESOURCES);
//				return null;
//			}
//		}
		
		return roles;
	}
	
	public Resources getResources() {
		if (getProject() == null) return null;
		/*
		 * We have project, now we have to get resources from session property
		 * or create it if we are first.
		 */
		Resources resources = null;
		try {
			resources = (Resources) getProject().getSessionProperty(SESSION_RESOURCES);
		} catch (CoreException e) {
			Logger.handle(MessageCode.RESOURCES_LOAD_DESERIALIZE_PROBLEM, e, SESSION_RESOURCES);
			return null;
		}
		if (resources == null) {
			IFile resourcesFile = project.getFile(ResourcesPersistence.DEFINITION_FILE);
			if (resourcesFile == null) return null;
			try {
				InputStream stream = resourcesFile.getContents();
				IPersistence<Resources> manager = PersistenceAdapter.getManager(Resources.class);
				resources = manager.loadFromStream(stream);
			} catch (CoreException e) {
				Logger.handle(MessageCode.RESOURCES_LOAD_DESERIALIZE_PROBLEM, e, resourcesFile.getName());
				return null;
			} catch (Exception e) {
				Logger.handle(MessageCode.RESOURCES_LOAD_DESERIALIZE_PROBLEM, e, resourcesFile.getName());
				return null;
			}
			
			try {
				project.setSessionProperty(SESSION_RESOURCES, resources);
			} catch (CoreException e) {
				Logger.handle(MessageCode.RESOURCES_SAVE_SERIALIZE_PROBLEM, e, SESSION_RESOURCES);
				return null;
			}
		}
		
		return resources;
	}
	
	public void clearResources() {
		if (getProject() == null) return;
		try {
			project.setSessionProperty(SESSION_RESOURCES, null);
		} catch (CoreException e) {
			Logger.handle(MessageCode.RESOURCES_CLEAR_PROBLEM, e, SESSION_RESOURCES);
			return;
		}
	}
	
	public HistoryFilesDescriptor getHistoryDescriptor() {
		if (getProject() == null) return null;
		/*
		 * We have project, now we have to get resources from session property
		 * or create it if we are first.
		 */
		HistoryFilesDescriptor history = null;
		try {
			history = (HistoryFilesDescriptor) getProject().getSessionProperty(SESSION_HISTORY);
		} catch (CoreException e) {
			Logger.handle(MessageCode.HISTORY_LOAD_DESCRIPTOR_PROBLEM, e, SESSION_HISTORY);
			return null;
		}
		if (history == null) {
			history = new HistoryFilesDescriptor(getProject());
			
			try {
				project.setSessionProperty(SESSION_HISTORY, history);
			} catch (CoreException e) {
				Logger.handle(MessageCode.HISTORY_LOAD_DESCRIPTOR_PROBLEM, e, SESSION_HISTORY);
				return null;
			}
		}
		
		return history;
	}

	public void clearHistoryDescriptor() {
		if (getProject() == null) return;
		try {
			project.setSessionProperty(SESSION_HISTORY, null);
		} catch (CoreException e) {
			Logger.handle(MessageCode.RESOURCES_CLEAR_PROBLEM, e, SESSION_HISTORY);
			return;
		}
	}

	/** Returns bucket or creates new if such does not exist, but also opens editor if it is not already
	 * opened.
	 * @param openEditor should the corresponding editor be opened as well
	 * @return Session bucket
	 */
	public Bucket getBucket(boolean openEditor) {
		
		if (getProject() == null) return null;
		/*
		 * We have project, now we have to get bucket from session property
		 * or create it if we are first.
		 */
		Bucket bucket = null;
		try {
			bucket = (Bucket) getProject().getSessionProperty(SESSION_BUCKET);
		} catch (CoreException e) {
			Logger.handle(MessageCode.BUCKET_LOAD_DESERIALIZE_PROBLEM, e, SESSION_BUCKET);
			return null;
		}

		IFile bucketFile = project.getFile(BucketPersistence.FILE);
		if (bucket == null) {
			if (bucketFile == null) return null;
			try {
				InputStream stream = bucketFile.getContents();
				IPersistence<Bucket> manager = PersistenceAdapter.getManager(Bucket.class);
				bucket = manager.loadFromStream(stream);
			} catch (CoreException e) {
				Logger.handle(MessageCode.BUCKET_LOAD_DESERIALIZE_PROBLEM, e, bucketFile.getName());
				return null;
			} catch (Exception e) {
				Logger.handle(MessageCode.BUCKET_LOAD_DESERIALIZE_PROBLEM, e, bucketFile.getName());
				return null;
			}
			
			try {
				project.setSessionProperty(SESSION_BUCKET, bucket);
			} catch (CoreException e) {
				Logger.handle(MessageCode.BUCKET_SAVE_SERIALIZE_PROBLEM, e, SESSION_BUCKET);
				return null;
			}
		}
		
		if (openEditor && bucketFile != null) {
			/* Opening editor */
			IWorkbench workbench = PlatformUI.getWorkbench();
			IEditorDescriptor bucketDesc = workbench.getEditorRegistry().getDefaultEditor(bucketFile.getName());
			IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage();
			
			FileEditorInput bucketEditorInput = new FileEditorInput(bucketFile);
			/* If editor is not yet opened just do it */
			if (page.findEditor(bucketEditorInput) == null) {
				try {
					page.openEditor(bucketEditorInput, bucketDesc.getId());
				} catch (PartInitException e) {
					Logger.handle(MessageCode.EDITOR_OPEN_PROBLEM, e, bucketDesc.getId(), bucketEditorInput.getName());
					return null;
				}
			}
		}
		
		return bucket;
	}

	public void clearBucket() {
		if (getProject() == null) return;
		try {
			project.setSessionProperty(SESSION_BUCKET, null);
		} catch (CoreException e) {
			Logger.handle(MessageCode.BUCKET_CLEAR_PROBLEM, e, SESSION_BUCKET);
			return;
		}
	}
}
